﻿#include <thread>
#include <QDebug>
#include <QImage>
#include <QAudioBuffer>
#include "StreamDecodec.h"

#define MAX_AUDIO_FRAME_SIZE 192000*2

StreamDecodec::StreamDecodec(QObject* parent /*= nullptr*/)
    : QObject(parent)
    , m_pFmtCtx(nullptr)
    , m_pViewCodec(nullptr)
    , m_pViewCodecCtx(nullptr)
    , m_pAudioCodecCtx(nullptr)
    , m_pViewStream(nullptr)
    , m_nViewIdx(-1)
    , m_pAudioStream(nullptr)
    , m_nAudioIdx(-1)
    , m_pImgConvertCtx(nullptr)
    , m_pAudioConvertCtx(nullptr)
    , m_bExitRead(false)
{
    init();
}

StreamDecodec::~StreamDecodec()
{

}

void StreamDecodec::setUrl(QString val)
{
    m_strUrl = val;
}

void StreamDecodec::play(QString url /*= ""*/)
{
    std::thread readThread(&StreamDecodec::threadReadThread, this, m_strUrl);

    readThread.detach();
}

bool StreamDecodec::getExitRead() const
{
    return m_bExitRead;
}

void StreamDecodec::setExitRead(bool val)
{
    m_bExitRead = val;
}

void StreamDecodec::init()
{

}

void StreamDecodec::threadReadThread(QString strUrl)
{
    AVPacket* pAvPktView = av_packet_alloc();
    //AVPacket* pAvPktAudio = av_packet_alloc();
    av_init_packet(pAvPktView);
    //av_init_packet(pAvPktAudio);
    
    int nRet = avformat_open_input(&m_pFmtCtx, strUrl.toLocal8Bit().data(), nullptr, nullptr);
    if (nRet != 0)
    {
        av_packet_free(&pAvPktView);
        pAvPktView = nullptr;
//         char buff[AV_ERROR_MAX_STRING_SIZE] = {0};
//         qDebug() << av_make_error_string(buff, AV_ERROR_MAX_STRING_SIZE, nRet);
        qDebug() << "open avformat input and allocate format context failed!";
        return;
    }

    if (avformat_find_stream_info(m_pFmtCtx, nullptr) < 0)
    {
        av_packet_free(&pAvPktView);
        pAvPktView = nullptr;
        qDebug() << "retrieve stream information failed!";
        return;
    }
    // 打印视频流信息
    av_dump_format(m_pFmtCtx, 0, strUrl.toLocal8Bit().data(), 0);

    m_nViewIdx = av_find_best_stream(m_pFmtCtx, AVMEDIA_TYPE_VIDEO, -1, -1, nullptr, 0);
    m_nAudioIdx = av_find_best_stream(m_pFmtCtx, AVMEDIA_TYPE_AUDIO, -1, -1, nullptr, 0);

    if (m_nViewIdx < 0 || m_nAudioIdx < 0)
    {
        av_packet_free(&pAvPktView);
        pAvPktView = nullptr;
        qDebug() << "could not find video and audio stream!";
        return;
    }
    m_pViewStream = m_pFmtCtx->streams[m_nViewIdx];
    m_pAudioStream = m_pFmtCtx->streams[m_nAudioIdx];

    // 查找视频流解码器
    m_pViewCodec = avcodec_find_decoder(m_pViewStream->codecpar->codec_id);
    // 查找音频流解码器
    m_pAudioCodec = avcodec_find_decoder(m_pAudioStream->codecpar->codec_id);

    if (m_pViewCodec == nullptr || m_pAudioCodec == nullptr)
    {
        av_packet_free(&pAvPktView);
        pAvPktView = nullptr;
        qDebug() << "could not find video and audio codec!";
        return;
    }
    
    m_pViewCodecCtx = avcodec_alloc_context3(m_pViewCodec);
    m_pAudioCodecCtx = avcodec_alloc_context3(m_pAudioCodec);

    int nViewRet = avcodec_parameters_to_context(m_pViewCodecCtx, m_pViewStream->codecpar);
    int nAudioRet = avcodec_parameters_to_context(m_pAudioCodecCtx, m_pAudioStream->codecpar);

    if (nViewRet < 0 || nAudioRet < 0)
    {
        av_packet_free(&pAvPktView);
        pAvPktView = nullptr;
        avcodec_free_context(&m_pViewCodecCtx);
        m_pViewCodecCtx = nullptr;
        avcodec_free_context(&m_pAudioCodecCtx);
        m_pAudioCodecCtx = nullptr;
        qDebug() << "copy codec parameters to decodec context failed!";
        return;
    }

    // 图片转换上下文
    m_pImgConvertCtx = sws_getContext(m_pViewCodecCtx->width, m_pViewCodecCtx->height, m_pViewCodecCtx->pix_fmt,
        m_pViewCodecCtx->width, m_pViewCodecCtx->height, AV_PIX_FMT_RGB24, SWS_BICUBIC, nullptr, nullptr, nullptr);

    // 音频转换上下文
    m_pAudioConvertCtx = swr_alloc();
//    int nnnn = m_pAudioCodecCtx->channels;
//    int out_sample_rate = 48000;
//    int64_t in_channel_layout = av_get_default_channel_layout(m_pAudioCodecCtx->channels);
    m_pAudioConvertCtx = swr_alloc_set_opts(m_pAudioConvertCtx, 
        AV_CH_LAYOUT_STEREO, AV_SAMPLE_FMT_S16, 48000,
        m_pAudioCodecCtx->channel_layout, m_pAudioCodecCtx->sample_fmt, m_pAudioCodecCtx->sample_rate,
        0, nullptr);
    swr_init(m_pAudioConvertCtx);

//    int out_channels = av_get_channel_layout_nb_channels(m_pAudioCodecCtx->channel_layout);
//     int bufferSize = av_samples_get_buffer_size(nullptr, m_pAudioCodecCtx->channels,
//         m_pAudioCodecCtx->frame_size, m_pAudioCodecCtx->sample_fmt, 1);
    
    uint8_t bufferOut[48000 * 4 * 2] = { 0 };
//     uint8_t* bufferOut = (uint8_t*)av_malloc(48000 * 4 * 2);
//     memset(bufferOut, 0, 48000 * 4 * 2);
//     uint8_t* bufferOut = (uint8_t*)malloc(bufferSize);
//     memset(bufferOut, 0, bufferSize);

    if (!m_pImgConvertCtx || !m_pAudioConvertCtx)
    {
        av_packet_free(&pAvPktView);
        pAvPktView = nullptr;
        avcodec_free_context(&m_pViewCodecCtx);
        m_pViewCodecCtx = nullptr;
        avcodec_free_context(&m_pAudioCodecCtx);
        m_pAudioCodecCtx = nullptr;
        qDebug() << "can not initialize the conversion context!";
        return;
    }

    nViewRet = avcodec_open2(m_pViewCodecCtx, m_pViewCodec, nullptr);
    nAudioRet = avcodec_open2(m_pAudioCodecCtx, m_pAudioCodec, nullptr);

    if (nViewRet < 0 || nAudioRet < 0)
    {
        av_packet_free(&pAvPktView);
        pAvPktView = nullptr;
        avcodec_free_context(&m_pViewCodecCtx);
        m_pViewCodecCtx = nullptr;
        avcodec_free_context(&m_pAudioCodecCtx);
        m_pAudioCodecCtx = nullptr;
        return;
    }

    AVFrame* pStreamFrame = av_frame_alloc();
    if (!pStreamFrame)
    {
        av_packet_free(&pAvPktView);
        pAvPktView = nullptr;
        avcodec_free_context(&m_pViewCodecCtx);
        m_pViewCodecCtx = nullptr;
        avcodec_free_context(&m_pAudioCodecCtx);
        m_pAudioCodecCtx = nullptr;
        qDebug() << "can not initialize the video frame!";
        return;
    }

    while (!m_bExitRead)
    {
        if (av_read_frame(m_pFmtCtx, pAvPktView) < 0)
        {
            break;
        }
        // 视频流解码
        viewStreamDecodec(pAvPktView, pStreamFrame);
        // 音频流解码
        audioStreamDecodec(pAvPktView, pStreamFrame, bufferOut);
        // 取消引用
        //av_packet_unref(pAvPktView);
        std::this_thread::sleep_for(std::chrono::milliseconds(20));
    }
    av_packet_free(&pAvPktView);
    pAvPktView = nullptr;
    av_frame_free(&pStreamFrame);
    pStreamFrame = nullptr;
//     avcodec_free_context(&m_pViewCodecCtx);
//     m_pViewCodecCtx = nullptr;
//     avcodec_free_context(&m_pAudioCodecCtx);
//     m_pAudioCodecCtx = nullptr;
//     av_free(bufferOut);
//     bufferOut = nullptr;
}

void StreamDecodec::audioStreamDecodec(AVPacket* pAvPktView, AVFrame* pStreamFrame, uint8_t* bufferOut)
{
    if (pAvPktView->stream_index != m_nAudioIdx)
    {
        return;
    }

    int gotFrame = 0;
    int nLen = avcodec_decode_audio4(m_pAudioCodecCtx, pStreamFrame, &gotFrame, pAvPktView);

    if (gotFrame)
    {
//         int data_size = av_get_bytes_per_sample(m_pAudioCodecCtx->sample_fmt);
//         if (data_size > 0)
//         {
// 
//         }
        int nSampleCount = swr_convert(m_pAudioConvertCtx,
            &bufferOut,
            MAX_AUDIO_FRAME_SIZE,
            (const uint8_t **)pStreamFrame->data,
            pStreamFrame->nb_samples);
        if (nSampleCount > 0)
        {
            QAudioFormat fmt;
            fmt.setCodec("audio/pcm");
            fmt.setSampleRate(48000);
            fmt.setChannelCount(2);
            fmt.setSampleSize(16);
            fmt.setSampleType(QAudioFormat::SignedInt);
            fmt.setByteOrder(QAudioFormat::LittleEndian);

            uint64_t out_channel_layout = AV_CH_LAYOUT_STEREO;
            int out_channels = av_get_channel_layout_nb_channels(out_channel_layout);

            int bufferSize = av_samples_get_buffer_size(nullptr, out_channels,
                m_pAudioCodecCtx->frame_size, AV_SAMPLE_FMT_S16, 0);

            QAudioBuffer audio(QByteArray((const char*)bufferOut, bufferSize), fmt);
            QVariant varAudio;
            varAudio.setValue(audio);

            emit sigStreamAudio(varAudio);
        }
    }
//     pAvPktView->size -= nLen;
//     pAvPktView->data += nLen;

//     pAvPktView->pts = AV_NOPTS_VALU
}

void StreamDecodec::viewStreamDecodec(AVPacket* pAvPktView, AVFrame* pStreamFrame)
{
    if (pAvPktView->stream_index != m_nViewIdx)
    {
        return;
    }
    int nLen = 0;
    int gotFrame = 0;
    nLen = avcodec_decode_video2(m_pViewCodecCtx, pStreamFrame, &gotFrame, pAvPktView);

    if (nLen > 0 && gotFrame > 0)
    {
        //构造一个QImage用作输出
        QImage imageOutput(m_pViewStream->codecpar->width, m_pViewStream->codecpar->height,
            QImage::Format_RGB888);
//         // 方法一
//         //构造AVFrame到QImage所需要的数据
//         int outputLineSize[4] = {0};
//         //  AV_PIX_FMT_RGB24 对应于 QImage::Format_RGB888
//         av_image_fill_linesizes(outputLineSize, AV_PIX_FMT_RGB24, m_pViewStream->codecpar->width);
//         uint8_t *outputDst[] = { imageOutput.bits() };
//         sws_scale(m_pImgConvertCtx, pViewFrame->data, pViewFrame->linesize,
//             0, m_pViewStream->codecpar->height, outputDst, outputLineSize);

        // 方法二
        AVFrame* pTempRGB = av_frame_alloc();
        //                 int nTempRGBSize = av_image_get_buffer_size(AV_PIX_FMT_RGB24, m_pViewStream->codecpar->width,
        //                     m_pViewStream->codecpar->height, 1);
        av_image_fill_arrays(pTempRGB->data, pTempRGB->linesize, imageOutput.bits(), AV_PIX_FMT_RGB24,
            m_pViewStream->codecpar->width, m_pViewStream->codecpar->height, 1);
        int nSliceHeight = sws_scale(m_pImgConvertCtx, pStreamFrame->data, pStreamFrame->linesize,
            0, m_pViewStream->codecpar->height, pTempRGB->data, pTempRGB->linesize);

        //pViewFrame->key_frame

        if (nSliceHeight > 0)
        {
            //                     static int i = 0;
            //                     imageOutput.save("image/" + QString::number(i++) + ".jpg");
            QVariant varView;
            varView.setValue(imageOutput);

            emit sigStreamView(varView);
        }
        av_frame_free(&pTempRGB);
        pTempRGB = nullptr;
    }
}
